#include "stdafx.h"
#include "SceneManage.h"


SceneManage::SceneManage()
{
}


SceneManage::~SceneManage()
{
}

SceneManage::emNodeType SceneManage::GetType(ptree tree)
{
	boost::optional<int> c = tree.get_optional<int>("NodeType");
	if (c.is_initialized())
	{
		return (emNodeType)c.get();
	}
	return TYPE_NONE;
}

ptree SceneManage::InitNodeTree(NodeData d, string sName)
{
	ptree ret;
	ret.put(m_strkeyWord[TYPE_NODE_TYPE], (int)TYPE_NODE);
	ret.put(m_strkeyWord[TYPE_NODE_DATA], d);
	ret.put(m_strkeyWord[TYPE_NODE_ISNEED_UPDATE], true);
	
	NodeData d1 = ret.get<NodeData>(m_strkeyWord[TYPE_NODE_DATA]);

	if (sName.empty())sName = CreateUUid();
	ret.put(m_strkeyWord[TYPE_NODE_NAME], sName);
	return ret;
}

string SceneManage::GetName(ptree tree)
{
	boost::optional<string> c = tree.get_optional<string>(m_strkeyWord[TYPE_NODE_NAME]);
	if (c.is_initialized())
	{
		return c.get();
	}
	return "";
}

ptree SceneManage::InitLeafTree(LeafData d, string sName)
{
	ptree ret;
	ret.put(m_strkeyWord[TYPE_NODE_TYPE], (int)TYPE_LEAF);
	ret.put(m_strkeyWord[TYPE_NODE_DATA], d);
	ret.put(m_strkeyWord[TYPE_NODE_ISNEED_UPDATE], true);


	if (sName.empty())sName = CreateUUid();
	ret.put(m_strkeyWord[TYPE_NODE_NAME], sName);
	return ret;
}

int SceneManage::AddNode(ptree& treeParent,NodeData d, string sName)
{
	int iRet = 1;
	if (GetType(treeParent) == TYPE_NODE)
	{
		ptree tree = InitNodeTree(d, sName);
		treeParent.add_child(GetName(tree), tree);
		iRet = 0;
	}
	return iRet;
}

int SceneManage::AddLeaf(ptree& treeParent, LeafData d, string sName)
{
	int iRet = 1;
	if (GetType(treeParent) == TYPE_NODE)
	{
		ptree tree = InitLeafTree(d, sName);
		treeParent.add_child(GetName(tree), tree);
		iRet = 0;
	}
	return iRet;
}


void SceneManage::CompileScene(PD3D12Device pDevice, DXGI_FORMAT backBufferFormat, DXGI_FORMAT depthStencilFormat, bool msaaState, UINT msaaQuality)
{
	EffectManage::GetInstance()->AddEffect("General", pDevice,  backBufferFormat, depthStencilFormat, msaaState, msaaQuality);
}

void SceneManage::InitScene()
{
	//texture
	{
		std::vector<std::string> texFilenames =
		{
			"Textures/bricks2.dds",
			"Textures/bricks2_nmap.dds",
			"Textures/tile.dds",
			"Textures/tile_nmap.dds",
			"Textures/white1x1.dds",
			"Textures/default_nmap.dds",
			"Textures/desertcube1024.dds"
		};

		std::vector<std::string> texNames =
		{
			"bricksDiffuseMap",
			"bricksNormalMap",
			"tileDiffuseMap",
			"tileNormalMap",
			"defaultDiffuseMap",
			"defaultNormalMap",
			"skyCubeMap"
		};
		for (int i = 0; i < texNames.size(); ++i)
		{
			PTextureObj pTex = PTextureObj(new TextureObj(texNames[i]));
			pTex->SetDDSFileName(texFilenames[i]);
			TextureObjManage::GetInstance()->AddTexture(pTex);
		}
	}


	//Material
	{
		shared_ptr<MaterialObj> pMaterial = shared_ptr<MaterialObj>(new MaterialObj("bricks"));
		pMaterial->SetTex(MaterialObj::TYPE_DIFFUSE, "bricksDiffuseMap");
		pMaterial->SetTex(MaterialObj::TYPE_NORMAL, "bricksNormalMap");
		pMaterial->DiffuseAlbedo(XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f));
		pMaterial->FresnelR0(XMFLOAT3(0.1f, 0.1f, 0.1f));
		pMaterial->Roughness(0.3f);
		MaterialManage::GetInstance()->Add(pMaterial->GetName(), pMaterial);

		pMaterial = shared_ptr<MaterialObj>(new MaterialObj("tile"));
		pMaterial->SetTex(MaterialObj::TYPE_DIFFUSE, "tileDiffuseMap");
		pMaterial->SetTex(MaterialObj::TYPE_NORMAL, "tileNormalMap");
		pMaterial->DiffuseAlbedo(XMFLOAT4(0.9f, 0.9f, 0.9f, 1.0f));
		pMaterial->FresnelR0(XMFLOAT3(0.2f, 0.2f, 0.2f));
		pMaterial->Roughness(0.1f);
		MaterialManage::GetInstance()->Add(pMaterial->GetName(), pMaterial);

		pMaterial = shared_ptr<MaterialObj>(new MaterialObj("default"));
		pMaterial->SetTex(MaterialObj::TYPE_DIFFUSE, "defaultDiffuseMap");
		pMaterial->SetTex(MaterialObj::TYPE_NORMAL, "defaultNormalMap");
		pMaterial->DiffuseAlbedo(XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f));
		pMaterial->FresnelR0(XMFLOAT3(0.98f, 0.97f, 0.95f));
		pMaterial->Roughness(0.1f);
		MaterialManage::GetInstance()->Add(pMaterial->GetName(), pMaterial);
	}

	//mesh
	StandMeshManage::GetInstance()->InitMesh();

	//light
	{
		vector<LightObj> v0bj;
		for (int i=0; i<25; ++i)
		{
			stringstream ss;
			ss << "Light" << i;
			LightObj l(ss.str());
			l.SetAsPnt(XMFLOAT3(0, 0, 0), 0.01, 5.0, XMFLOAT3(0.8, 0.8, 0.8));
			LightManage::GetLightBase()->Add(l.GetName(), l);
		}

	}

	//camera
	{
		//vector<CameraConstants> vCam;
		shared_ptr<CameraConstants> pCam = shared_ptr<CameraConstants>(new CameraConstants("MainCam"));
		pCam->LookAt(XMFLOAT3(0, 0, 0), XMFLOAT3(0, 0, 1), XMFLOAT3(0, 1, 0));
		CameraManage::GetInstance()->Add(pCam->GetName(), pCam);
	}

	//shader
	{
		//ShaderManage::GetInstance()->AddShader("stand", "Shaders\\Default.hlsl", VertexObj::GetTypeName());
		//ShaderManage::GetInstance()->AddShader("normal", "Shaders\\DrawNormals.hlsl", VertexObj::GetTypeName());
		ShaderManage::GetInstance()->AddShader("stand", "Shaders\\CommonInput.hlsl", VertexObj::GetTypeName());
		ShaderManage::GetInstance()->Compile();

	}

	//tree
	int k = 0;
	m_treeRoot = InitNodeTree(NodeData(MathHelper::Identity4x4()), "root");
	NodeData& d = m_treeRoot.get<NodeData>(m_strkeyWord[TYPE_NODE_DATA]);
	string sMateriName[] = { "bricks" , "tile", "default" };
	for (int i=0; i<5; ++i)
	{
		for (int j=0; j<5; ++j)
		{
			XMFLOAT4X4 mat = MathHelper::Translation(i * 30 , j * 30, 0);
			
			string sLight = string("Light") + to_string(i * 5 + j);
			string sNodeName = string("Node") + to_string(i * 5 + j);
			AddNode(m_treeRoot, NodeData(mat, sLight), sNodeName);

			ptree& node = m_treeRoot.get_child(sNodeName);
			string sShaderName = "stand";
			if (j > 2) sShaderName = "normal";
			sNodeName = string("Leaf") + to_string(i * 5 + j);
			AddLeaf(node, LeafData("Box", sMateriName[k % 3], sShaderName), sNodeName);
			++k;
		}
	}
}

void SceneManage::UpdateNode()
{
	stack<pair<ptree*, XMFLOAT4X4 >> stackData;

	stackData.push(make_pair(&m_treeRoot, MathHelper::Identity4x4()));
	while (!stackData.empty())
	{
		pair<ptree*, XMFLOAT4X4 > paData = stackData.top();
		ptree*pTree = paData.first;
		XMFLOAT4X4 mat = paData.second;
		stackData.pop();
		ptree& vChild = pTree->get_child("");
		bool isNeedUpdate = pTree->get<bool>(m_strkeyWord[TYPE_NODE_ISNEED_UPDATE]);

		for (auto pos = vChild.begin(); pos != vChild.end(); ++pos)
		{
			if (GetKeyType(pos->first) == TYPE_NODE_MAX)
			{
				ptree& childData = pos->second;
				string sName = GetName(childData);
				if(isNeedUpdate) childData.put<bool>(m_strkeyWord[TYPE_NODE_ISNEED_UPDATE], isNeedUpdate);
				int iType = childData.get<int>(m_strkeyWord[TYPE_NODE_TYPE]);
				if (iType == TYPE_NODE)
				{
					NodeData d = childData.get<NodeData>(m_strkeyWord[TYPE_NODE_DATA]);
					if (isNeedUpdate)
					{
						d.Update(mat);
						childData.put(m_strkeyWord[TYPE_NODE_DATA], d);
					}

					stackData.push(make_pair(&childData, d.GetMatWorld()));
				}
				else
				{
					LeafData d = childData.get<LeafData>(m_strkeyWord[TYPE_NODE_DATA]);
					if (isNeedUpdate)
					{
						d.Update(mat);
						childData.put(m_strkeyWord[TYPE_NODE_DATA], d);
					}
					int i = 0;
				}
			}
		}

	}
}

vector<ScenRenderItem> SceneManage::GetRenderItem( PD3D12Device devicer, PCommandList pCommandList, string sCamName)
{
	UpdateNode();
	struct suStackData
	{
		list<NodeData> m_lsData;
		ptree* m_treeNode;
	};

	stack<suStackData> stackData;
	suStackData d;
	d.m_treeNode = &m_treeRoot;
	stackData.push(d);
	vector<ScenRenderItem> ret;
	while (!stackData.empty())
	{
		suStackData dataStack = stackData.top();
		stackData.pop();
		ptree& vChild = dataStack.m_treeNode->get_child("");
		for (auto pos = vChild.begin(); pos != vChild.end(); ++pos)
		{
			if (GetKeyType(pos->first) == TYPE_NODE_MAX)
			{
				ptree& childData = pos->second;
				string sName = childData.get<string>("Name");
				int iType = childData.get<int>("NodeType");
				if (iType == TYPE_NODE)
				{
					suStackData dataPush = dataStack;
					NodeData nodeData = childData.get<NodeData>(m_strkeyWord[TYPE_NODE_DATA]);
					dataPush.m_treeNode = &(pos->second);
					dataPush.m_lsData.push_back(nodeData);
					stackData.push(dataPush);
				}
				else
				{
					LeafData leafData;
					string sName = childData.get<string>("Name");
					leafData = childData.get<LeafData>("Data");
					ScenRenderItem item;
					item.m_matWorld = leafData.GetMatWorld();
					item.m_pStandGeo = shared_ptr<StandGeometry>(new StandGeometry(devicer, pCommandList));
					item.m_pStandGeo->SetMesh(leafData.GetMesh());
				//	item.m_pStandGeo->UpdateBuffer();

					//
					item.m_pMaterialObj = MaterialManage::GetInstance()->Get(leafData.GetMaterial());

					item.m_strPipeShaderName = "stand";
					vector<string> vStrLightName;
					for (const NodeData& d : dataStack.m_lsData)
					{
						list<string> sName = d.GetLight();
						vStrLightName.insert(vStrLightName.end(), sName.begin(), sName.end());
					}
					item.SetLight(vStrLightName);
					vector<string> vStr = item.m_pStandGeo->GetSubMeshName();
					for (string str : vStr)
					{
						item.m_inxCBObj = ret.size();
						item.SetDrawIndexData(item.m_pStandGeo->GetSubMeshInfo(str));
						ret.push_back(item);
					}
				}
			}

		}
	}
	return ret;
}
